﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using UDS.Components;
using System.Web;
using System.IO;

namespace UDS.Entity
{
    public class UDSMail
    {
        public const string MailAttachmentFolder = @"~/Files/Mail/";
        /// <summary>
        /// Send an email.
        /// </summary>
        /// <returns>
        /// The unique identifier of the mail.
        /// </returns>
        public static int SendMail(MailFullInfo mailbody, HttpFileCollection attachments,bool sendSMS)
        {
            if (mailbody == null)
            {
                throw new ArgumentNullException("mailbody");
            }
            if (string.IsNullOrEmpty(mailbody.Sender))
            {
                throw new ArgumentException("The sender is required.", "mailbody");
            }

            string[] receiverList = Utility.MergeArray(
                StringComparer.InvariantCultureIgnoreCase,
                Utility.SplitArray(mailbody.TOList),
                Utility.SplitArray(mailbody.CCList),
                Utility.SplitArray(mailbody.BCCList));

            /*
             * XML format
             * <recipients>
             *  <r name="" />
             * </recipients>
             */
            string xml = Utility.GenerateXML(receiverList, "recipients", "r", "name");

            var parameters = new[]
            {
                Database.MakeOutParam("@MailBodyID", SqlDbType.Int, 4),
                Database.MakeInParam("@Sender", SqlDbType.NVarChar, 50, mailbody.Sender),                
                Database.MakeInParam("@TOList", SqlDbType.NVarChar, 1024, mailbody.TOList),
                Database.MakeInParam("@CCList", SqlDbType.NVarChar, 1024, mailbody.CCList),
                Database.MakeInParam("@BCCList", SqlDbType.NVarChar, 1024, mailbody.BCCList),
                Database.MakeInParam("@RecipientXml", SqlDbType.Xml, -1, xml),
                Database.MakeInParam("@Subject", SqlDbType.NVarChar, 256, mailbody.Subject),
                Database.MakeInParam("@Body", SqlDbType.NText, -1, mailbody.Body),                
                Database.MakeInParam("@ClassID", SqlDbType.Int, 4, mailbody.ClassID),
                Database.MakeInParam("@Importance", SqlDbType.Int, 4, (int)mailbody.Importance),
                Database.MakeInParam("@SendSMS", SqlDbType.Bit, 1, sendSMS),
            };

            int mailBodyId = -1;
            DBAction action = delegate
            {
                mailBodyId = SendMailInternal(parameters, attachments);
            };
            Database.RunWithRetry(action);
            return mailBodyId;
        }

        private static int SendMailInternal(SqlParameter[] parameters, HttpFileCollection attachments)
        {
            int mailBodyId = -1;
            using (var db = new Database())
            {
                using (db.BeginTransaction())
                {
                    db.RunProc("dbo.sproc_Mail_SendMail_V3", parameters);

                    var p = parameters[0];
                    if (p.Value == DBNull.Value)
                    {
                        db.Rollback();
                        throw new InvalidOperationException("Failed to send mail.");
                    }

                    mailBodyId = (int)p.Value;
                    if (attachments != null && attachments.Count > 0)
                    {
                        string rootVDir = Utility.CombineVirtualDirs(MailAttachmentFolder, mailBodyId.ToString());
                        List<HttpPostedFile> validFiles = new List<HttpPostedFile>(attachments.Count);
                        for (int i = 0; i < attachments.Count; i++)
                        {
                            HttpPostedFile file = attachments[i];
                            if (string.IsNullOrEmpty(file.FileName) || file.ContentLength == 0)
                            {
                                continue;
                            }

                            // Create the file in database
                            MailAttachmentInfo info = new MailAttachmentInfo();
                            info.MailBodyID = mailBodyId;
                            info.FileName = Path.GetFileName(file.FileName);
                            info.VirtualPath = Utility.CombineVirtualDirs(rootVDir, info.FileName);
                            info.FileSize = file.ContentLength;
                            info.FileAttribute = 0;  // alreays set it as zero for now.
                            CreateMailAttachment(db, info);

                            validFiles.Add(file);
                        }

                        // Save all the files to local disk
                        if (validFiles.Count > 0)
                        {
                            // Ensure the directory exists
                            string physicalDir = HttpUtil.MapPath(rootVDir);
                            DirectoryInfo dirInfo = new DirectoryInfo(physicalDir);
                            if (!dirInfo.Exists)
                            {
                                dirInfo.Create();
                            }

                            foreach (HttpPostedFile file in validFiles)
                            {
                                file.SaveAs(Path.Combine(physicalDir, Path.GetFileName(file.FileName)));
                            }
                        }
                    }

                    db.Commit();
                }
            }
            return mailBodyId;
        }

        public static List<MailInfo> GetMails(string userName, MailFolderType folderType)
        {
            var parameters = new[]
            {
                Database.MakeInParam("@UserName", SqlDbType.NVarChar, 50, userName),
                Database.MakeInParam("@FolderType", SqlDbType.Int, 4, (int)folderType)
            };

            List<MailInfo> list = new List<MailInfo>();
            using (var data = new Database())
            {
                using (IDataReader dr = data.RunDataReader("dbo.sproc_Mail_GetBriefInfo_V2", parameters))
                {
                    while (dr.Read())
                    {
                        list.Add(MailInfo.ReadMailInfo(dr));
                    }
                }
            }
            return list;
        }

        public static MailAttachmentInfo GetMailAttachment(int fileId)
        {
            // create data object and params
            var parameters = new[]
            {
                Database.MakeInParam("@FileID", SqlDbType.Int, 4, fileId)
            };

            using (var db = new Database())
            {
                // run the stored procedure
                using (IDataReader dr = db.RunDataReader("dbo.sproc_Mail_GetAttachmentInfo_V2", parameters))
                {
                    if (dr.Read())
                    {
                        return MailAttachmentInfo.ReadAttachmentInfo(dr);
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Get the full information about the given mail, and also remove its new flag.
        /// </summary>
        public static MailFullInfo GetMailFullInfo(int mailId)
        {
            // create data object and params
            var parameters = new[]
            {
                Database.MakeInParam("@MailID", SqlDbType.Int, 4, mailId)
            };

            using (var db = new Database())
            {
                // run the stored procedure
                using (IDataReader dr = db.RunDataReader("dbo.sproc_Mail_GetCompleteInfo_V2", parameters))
                {
                    if (dr.Read())
                    {
                        return MailFullInfo.ReadMailInfo(dr);
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Remove an array of emails.
        /// </summary>
        /// <param name="mails">
        /// The identifiers of the mails to be deleted.
        /// </param>
        /// <param name="physicalRemove">
        /// "true" to physically remove the mails, or "false" to move them to recycle bin.
        /// </param>
        public static void DeleteMail(int[] mails, bool physicalRemove)
        {
            if (mails != null && mails.Length > 0)
            {
                /*
                 * XML format
                 * <mails>
                 *  <m id="" />
                 * </mails>
                 */
                string xml = Utility.GenerateXML(mails, "mails", "m", "id");
                var parameters = new[]
                {
                    Database.MakeInParam("@mailIdXml", SqlDbType.Xml, -1, xml),
                    Database.MakeInParam("@physicalRemove", SqlDbType.Bit, 1, physicalRemove)
                };

                RemoveMailAndAttachments("dbo.sproc_Mail_Delete_V2", parameters);
            }
        }

        /// <summary>
        /// Clear the given mailbox for the specified user.
        /// </summary>
        public static void ClearFolder(string userName, MailFolderType folderType)
        {
            var db = new Database();
            var parameters = new[]
            {
                Database.MakeInParam("@FolderType", SqlDbType.Int, 4, (int)folderType),
                Database.MakeInParam("@Username", SqlDbType.NVarChar, 50, userName)
            };

            RemoveMailAndAttachments("dbo.sproc_Mail_ClearFolder_V2", parameters);
        }

        private static void CreateMailAttachment(Database db, MailAttachmentInfo info)
        {
            var parameters = new[]
            {
                Database.MakeInParam("@MailBodyID", SqlDbType.Int, 4, info.MailBodyID),
                Database.MakeInParam("@FileName", SqlDbType.NVarChar, 256, info.FileName),
                Database.MakeInParam("@VirtualPath", SqlDbType.NVarChar, 512, info.VirtualPath),
                Database.MakeInParam("@FileSize", SqlDbType.Int, 4, info.FileSize),
                Database.MakeInParam("@FileAttribute", SqlDbType.Int, 4, info.FileAttribute),
            };

            db.RunProc("dbo.sproc_Mail_AddAttachment_V2", parameters);
        }

        private static void RemoveMailAndAttachments(string sprocName, SqlParameter[] parameters)
        {
            using (var db = new Database())
            {
                // Create a new transaction to finish the job.
                using (db.BeginTransaction())
                {
                    using (var dr = db.RunDataReader(sprocName, parameters))
                    {
                        while (dr.Read())
                        {
                            // TODO:
                            // Remove the attachment files
                        }
                    }

                    // Commit the transaction
                    db.Commit();
                }
            }
        }
    }
}
